﻿using Common;
using JumpKick.HttpLib;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace AutoUpdateClient
{
    public delegate void StatusCallback(object obj);

    public enum UpdateDataType
    {
        Status,
        Detailed,
        Progress,
        SystemMessage,
    }

    public enum SystemMessage
    {
        UpdateError,
        Finsh,
    }

    public class AutoUpdateHelper
    {
        /// <summary>
        /// 网络xml路径
        /// </summary>
        public string WebXmlUrl { get; set; }

        /// <summary>
        /// 配置文件路径
        /// </summary>
        public string ConfigXmlPath { get; set; }

        /// <summary>
        /// 临时文件路径
        /// </summary>
        public string TempXmlPath { get; set; }

        /// <summary>
        /// 相关文件的配置路径
        /// </summary>
        public string FilePath { get; set; }

        /// <summary>
        /// 是否成功
        /// </summary>
        public bool IsSuccess { get; private set; }

        /// <summary>
        /// 是否完成
        /// </summary>
        public bool IsDone { get; private set; }

        /// <summary>
        /// 打开旧版本，当升级失败时
        /// </summary>
        public bool OpenOldWhenError { get; set; }

        /// <summary>
        /// 当升级失败打开旧版本时，是否确认
        /// </summary>
        public bool ConfirmOpenOldWhenError { get; set; }

        private void Assert(bool assert, string error_message)
        {
            if (!assert)
                throw new Exception(error_message);
        }

        /// <summary>
        /// 状态回调
        /// </summary>
        public StatusCallback CallBack { get; set; }

        /// <summary>
        /// 安全调用回调
        /// </summary>
        /// <param name="dic"></param>
        private void SafeCall(Dictionary<UpdateDataType, object> dic)
        {
            if (CallBack != null)
                CallBack(dic);
        }

        private string GetFinalPath(string path = null)
        {
            string RootPath = FilePath;
            if (string.IsNullOrEmpty(path))
                return RootPath;
            else
                return Path.Combine(RootPath, path);
        }

        /// <summary>
        /// 开始相关的处理
        /// </summary>
        public void Start()
        {
            Assert(!string.IsNullOrEmpty(WebXmlUrl), "没有找到网络URL");
            Assert(!string.IsNullOrEmpty(ConfigXmlPath), "没有找到本地配置文件路径");
            Assert(!string.IsNullOrEmpty(TempXmlPath), "没有找到本地临时文件路径");
            Assert(!string.IsNullOrEmpty(FilePath), "没有找到本地文件路径");

            if (!Directory.Exists(FilePath))
                Directory.CreateDirectory(FilePath);

            Action<string, string> SaveStringToFile = (httpdata, filepath) =>
            {
                StreamWriter sw = new StreamWriter(filepath);
                sw.Write(httpdata);
                sw.Close();
            };

            Http.Get(WebXmlUrl).OnSuccess(result =>
            {
                SafeCall(new Dictionary<UpdateDataType, object> { { UpdateDataType.Status, "数据加载完成" } });

                var synchronize_versions = JsonConvert.DeserializeObject<SynchronizeVersions>(result);
                var versions_error = synchronize_versions.TimeStamp == 0 || string.IsNullOrEmpty(synchronize_versions.VersionsCheckCode);
                var have_temp_file = File.Exists(TempXmlPath);

                SynchronizeCache synchronize_cache = null;
                if (have_temp_file)
                {
                    var sr = new StreamReader(TempXmlPath);
                    var cache_data = sr.ReadToEnd();
                    sr.Close();
                    synchronize_cache = JsonConvert.DeserializeObject<SynchronizeCache>(cache_data);

                    if (!versions_error &&
                            (synchronize_cache.TimeStamp != synchronize_versions.TimeStamp
                            || synchronize_cache.VersionsCheckCode != synchronize_versions.VersionsCheckCode))
                    {
                        synchronize_cache = null;
                    }
                }

                if (versions_error)
                {
                    SafeCall(new Dictionary<UpdateDataType, object> { { UpdateDataType.Status, "获取网络配置信息失败" } });

                    // 判定预设怎么处理
                    // 如果连最开始的版本都不存在则不能进行后续的启动处理

                    Action UpdateError = () =>
                    {
                        SafeCall(new Dictionary<UpdateDataType, object> { 
                            { UpdateDataType.Status, "获取网络配置信息失败" },
                            { UpdateDataType.SystemMessage, SystemMessage.UpdateError },
                        });
                    };

                    Action OpenOldVersion = () =>
                    {

                    };

                    if (OpenOldWhenError && have_temp_file && synchronize_cache != null)
                    {
                        var open_old = !ConfirmOpenOldWhenError || MessageBox.Show("升级检查失败，是否打开旧版本？", "系统提示", MessageBoxButtons.YesNo) == DialogResult.Yes;
                        if (!open_old)
                        {
                            UpdateError();
                        }
                        else
                        {
                            OpenOldVersion();
                        }
                    }
                    else
                    {
                        UpdateError();
                    }
                    return;
                }

                if (synchronize_cache == null)
                {
                    synchronize_cache = new SynchronizeCache();
                    synchronize_cache.TimeStamp = synchronize_versions.TimeStamp;
                    synchronize_cache.VersionsCheckCode = synchronize_versions.VersionsCheckCode;
                    synchronize_cache.CacheInfos = (synchronize_versions.FileInfos ?? new List<Common.FileInfo>()).Select(n =>
                    {
                        var FinalPath = GetFinalPath(n.FilePath);
                        var have_file = File.Exists(FinalPath);
                        var cache_info = new Common.FileInfo
                        {
                            FileMD5 = n.FileMD5,
                            FilePath = n.FilePath,
                            FileUrl = n.FileUrl,
                            IsInLocal = have_file && FileHelper.GetMd5(FinalPath) == n.FileMD5,
                        };

                        return cache_info;
                    });
                }

                SaveStringToFile(result, ConfigXmlPath);

                SafeCall(new Dictionary<UpdateDataType, object> { { UpdateDataType.Status, "正在收集配置信息" } });

                // 清理不相关文件
                foreach (var item in synchronize_cache.CacheInfos)
                {
                    var final_path = GetFinalPath(item.FilePath);

                    if (!item.IsInLocal && File.Exists(final_path))
                        File.Delete(final_path);
                }

                var all_files = FileHelper.GetAllChildFilePath(FilePath);
                foreach (var item in all_files)
                {
                    if (synchronize_cache.CacheInfos.All(n => GetFinalPath(n.FilePath) != item))
                        File.Delete(item);
                }

                var cache_infos = synchronize_cache.CacheInfos.Where(n => !n.IsInLocal);

                if (cache_infos.Count() > 0) 
                {
                    SafeCall(new Dictionary<UpdateDataType, object> { 
                        { UpdateDataType.Status, string.Format("正在尝试从服务器拉取相应文件({0})", cache_infos.Count()) } 
                    });
                }

                var _lock = new object();

                int AllCount = cache_infos.Count();
                int FinshCount = 0;

                foreach (var item in cache_infos)
                {
                    var FinalPath = GetFinalPath(item.FilePath);
                    var ParentPath = Directory.GetParent(FinalPath).FullName;
                    if (!Directory.Exists(ParentPath))
                        Directory.CreateDirectory(ParentPath);

                    Http.Get(synchronize_versions.ServerUrl + "/" + item.FileUrl).DownloadTo(FinalPath,
                        (bytesCopied, totalBytes) =>
                        {
                            var dic = new Dictionary<UpdateDataType, object>();
                            if (totalBytes.HasValue)
                            {
                                dic.Add(UpdateDataType.Detailed, string.Format("{0}（{1}）", item.FilePath, bytesCopied / totalBytes * 100 + "%"));
                            }
                            else
                            {
                                dic.Add(UpdateDataType.Detailed, string.Format("{0}（{1}）", item.FilePath, bytesCopied));
                            }

                            SafeCall(dic);
                        },
                        (headers) =>
                        {
                            lock (_lock)
                                FinshCount++;
                        }
                    ).Go();
                }

                while (AllCount != FinshCount)
                {
                    var dic = new Dictionary<UpdateDataType, object>();
                    dic.Add(UpdateDataType.Status, string.Format("{0}/{1}", FinshCount, AllCount));
                    SafeCall(dic);

                    Thread.Sleep(100);
                }

                SafeCall(new Dictionary<UpdateDataType, object> { 
                    { UpdateDataType.Status, "Finsh Now" } 
                });

                SafeCall(new Dictionary<UpdateDataType, object> { { UpdateDataType.SystemMessage, SystemMessage.Finsh } });

            }).OnFail(result =>
            {
                SafeCall(new Dictionary<UpdateDataType, object> { { UpdateDataType.Status, "获取网络数据失败" } });
            }).Go();
        }
    }
}
